package validate

import (
	"sync"

	"github.com/go-openapi/spec"
)

var (
	// memory pools for all validator objects.
	//
	// Each pool can be borrowed from and redeemed to.
	poolOfSchemaValidators      schemaValidatorsPool
	poolOfObjectValidators      objectValidatorsPool
	poolOfSliceValidators       sliceValidatorsPool
	poolOfItemsValidators       itemsValidatorsPool
	poolOfBasicCommonValidators basicCommonValidatorsPool
	poolOfHeaderValidators      headerValidatorsPool
	poolOfParamValidators       paramValidatorsPool
	poolOfBasicSliceValidators  basicSliceValidatorsPool
	poolOfNumberValidators      numberValidatorsPool
	poolOfStringValidators      stringValidatorsPool
	poolOfSchemaPropsValidators schemaPropsValidatorsPool
	poolOfFormatValidators      formatValidatorsPool
	poolOfTypeValidators        typeValidatorsPool
	poolOfSchemas               schemasPool
	poolOfResults               resultsPool
)

func init() {
	resetPools()
}

func resetPools() {
	// NOTE: for testing purpose, we might want to reset pools after calling Validate twice.
	// The pool is corrupted in that case: calling Put twice inserts a duplicate in the pool
	// and further calls to Get are mishandled.
	poolOfSchemaValidators = schemaValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &SchemaValidator{}

				return s
			},
		},
	}

	poolOfObjectValidators = objectValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &objectValidator{}

				return s
			},
		},
	}

	poolOfSliceValidators = sliceValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &schemaSliceValidator{}

				return s
			},
		},
	}

	poolOfItemsValidators = itemsValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &itemsValidator{}

				return s
			},
		},
	}

	poolOfBasicCommonValidators = basicCommonValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &basicCommonValidator{}

				return s
			},
		},
	}

	poolOfHeaderValidators = headerValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &HeaderValidator{}

				return s
			},
		},
	}

	poolOfParamValidators = paramValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &ParamValidator{}

				return s
			},
		},
	}

	poolOfBasicSliceValidators = basicSliceValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &basicSliceValidator{}

				return s
			},
		},
	}

	poolOfNumberValidators = numberValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &numberValidator{}

				return s
			},
		},
	}

	poolOfStringValidators = stringValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &stringValidator{}

				return s
			},
		},
	}

	poolOfSchemaPropsValidators = schemaPropsValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &schemaPropsValidator{}

				return s
			},
		},
	}

	poolOfFormatValidators = formatValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &formatValidator{}

				return s
			},
		},
	}

	poolOfTypeValidators = typeValidatorsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &typeValidator{}

				return s
			},
		},
	}

	poolOfSchemas = schemasPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &spec.Schema{}

				return s
			},
		},
	}

	poolOfResults = resultsPool{
		Pool: &sync.Pool{
			New: func() any {
				s := &Result{}

				return s
			},
		},
	}
}

type (
	schemaValidatorsPool struct {
		*sync.Pool
	}

	objectValidatorsPool struct {
		*sync.Pool
	}

	sliceValidatorsPool struct {
		*sync.Pool
	}

	itemsValidatorsPool struct {
		*sync.Pool
	}

	basicCommonValidatorsPool struct {
		*sync.Pool
	}

	headerValidatorsPool struct {
		*sync.Pool
	}

	paramValidatorsPool struct {
		*sync.Pool
	}

	basicSliceValidatorsPool struct {
		*sync.Pool
	}

	numberValidatorsPool struct {
		*sync.Pool
	}

	stringValidatorsPool struct {
		*sync.Pool
	}

	schemaPropsValidatorsPool struct {
		*sync.Pool
	}

	formatValidatorsPool struct {
		*sync.Pool
	}

	typeValidatorsPool struct {
		*sync.Pool
	}

	schemasPool struct {
		*sync.Pool
	}

	resultsPool struct {
		*sync.Pool
	}
)

func (p schemaValidatorsPool) BorrowValidator() *SchemaValidator {
	return p.Get().(*SchemaValidator)
}

func (p schemaValidatorsPool) RedeemValidator(s *SchemaValidator) {
	// NOTE: s might be nil. In that case, Put is a noop.
	p.Put(s)
}

func (p objectValidatorsPool) BorrowValidator() *objectValidator {
	return p.Get().(*objectValidator)
}

func (p objectValidatorsPool) RedeemValidator(s *objectValidator) {
	p.Put(s)
}

func (p sliceValidatorsPool) BorrowValidator() *schemaSliceValidator {
	return p.Get().(*schemaSliceValidator)
}

func (p sliceValidatorsPool) RedeemValidator(s *schemaSliceValidator) {
	p.Put(s)
}

func (p itemsValidatorsPool) BorrowValidator() *itemsValidator {
	return p.Get().(*itemsValidator)
}

func (p itemsValidatorsPool) RedeemValidator(s *itemsValidator) {
	p.Put(s)
}

func (p basicCommonValidatorsPool) BorrowValidator() *basicCommonValidator {
	return p.Get().(*basicCommonValidator)
}

func (p basicCommonValidatorsPool) RedeemValidator(s *basicCommonValidator) {
	p.Put(s)
}

func (p headerValidatorsPool) BorrowValidator() *HeaderValidator {
	return p.Get().(*HeaderValidator)
}

func (p headerValidatorsPool) RedeemValidator(s *HeaderValidator) {
	p.Put(s)
}

func (p paramValidatorsPool) BorrowValidator() *ParamValidator {
	return p.Get().(*ParamValidator)
}

func (p paramValidatorsPool) RedeemValidator(s *ParamValidator) {
	p.Put(s)
}

func (p basicSliceValidatorsPool) BorrowValidator() *basicSliceValidator {
	return p.Get().(*basicSliceValidator)
}

func (p basicSliceValidatorsPool) RedeemValidator(s *basicSliceValidator) {
	p.Put(s)
}

func (p numberValidatorsPool) BorrowValidator() *numberValidator {
	return p.Get().(*numberValidator)
}

func (p numberValidatorsPool) RedeemValidator(s *numberValidator) {
	p.Put(s)
}

func (p stringValidatorsPool) BorrowValidator() *stringValidator {
	return p.Get().(*stringValidator)
}

func (p stringValidatorsPool) RedeemValidator(s *stringValidator) {
	p.Put(s)
}

func (p schemaPropsValidatorsPool) BorrowValidator() *schemaPropsValidator {
	return p.Get().(*schemaPropsValidator)
}

func (p schemaPropsValidatorsPool) RedeemValidator(s *schemaPropsValidator) {
	p.Put(s)
}

func (p formatValidatorsPool) BorrowValidator() *formatValidator {
	return p.Get().(*formatValidator)
}

func (p formatValidatorsPool) RedeemValidator(s *formatValidator) {
	p.Put(s)
}

func (p typeValidatorsPool) BorrowValidator() *typeValidator {
	return p.Get().(*typeValidator)
}

func (p typeValidatorsPool) RedeemValidator(s *typeValidator) {
	p.Put(s)
}

func (p schemasPool) BorrowSchema() *spec.Schema {
	return p.Get().(*spec.Schema)
}

func (p schemasPool) RedeemSchema(s *spec.Schema) {
	p.Put(s)
}

func (p resultsPool) BorrowResult() *Result {
	return p.Get().(*Result).cleared()
}

func (p resultsPool) RedeemResult(s *Result) {
	if s == emptyResult {
		return
	}
	p.Put(s)
}
